package com.qwiki.xml;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * 
 */

/**
 * WikiXmlEngine
 * 
 * @author xgu
 * @since Jun 26, 2012
 */
public class WikiXmlEngine implements ContentHandler
{
  private final String filename;

  private XMLReader XMLReader;
  private boolean inpage;
  private WikiXMLPage currentPage;
  private ArrayList<String> currentState = new ArrayList<String>();
  private StringBuilder currentSB;
  private WikiPageHandler handler;

  public WikiXmlEngine(String filename)
  {
    this.filename = filename;
  }

  public void setHandler(WikiPageHandler handler)
  {
    this.handler = handler;
  }

  public void process() throws SAXException, IOException
  {

    FileInputStream fis = new FileInputStream(filename);
    BufferedReader reader = new BufferedReader(new InputStreamReader(fis, "UTF-8"), 1024 * 1024);
    XMLReader = XMLReaderFactory.createXMLReader();
    XMLReader.setContentHandler(this);
    // XMLReader.setErrorHandler(this);
    XMLReader.parse(new InputSource(reader));

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
   */
  @Override
  public void setDocumentLocator(Locator locator)
  {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#startDocument()
   */
  @Override
  public void startDocument() throws SAXException
  {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#endDocument()
   */
  @Override
  public void endDocument() throws SAXException
  {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
   * java.lang.String)
   */
  @Override
  public void startPrefixMapping(String prefix, String uri) throws SAXException
  {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
   */
  @Override
  public void endPrefixMapping(String prefix) throws SAXException
  {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
   * java.lang.String, java.lang.String, org.xml.sax.Attributes)
   */
  @Override
  public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException
  {
    if ("page".equals(qName))
    {
      if (inpage)
        throw new RuntimeException("already inside a page");
      inpage = true;
      currentPage = new WikiXMLPage();
      currentState.add(qName);
    } else if ("ns".equals(qName))
    {
      currentState.add(qName);
      currentSB = new StringBuilder();
    } else if ("id".equals(qName))
    {
      currentState.add(qName);
      currentSB = new StringBuilder();
    } else if ("title".equals(qName))
    {
      currentState.add(qName);
      currentSB = new StringBuilder();
    } else if ("text".equals(qName))
    {
      currentState.add(qName);
      currentSB = new StringBuilder();
    } else if ("redirect".equals(qName))
    {
      currentState.add(qName);
      currentSB = new StringBuilder();
      currentSB.append(atts.getValue("title"));
    } else
    {
      currentState.add(qName);
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
   * java.lang.String, java.lang.String)
   */
  @Override
  public void endElement(String uri, String localName, String qName) throws SAXException
  {
    if ("page".equals(qName) && currentState.size() == 2)
    {
      if (!inpage)
        throw new RuntimeException("not inside a page yet");
      inpage = false;
      if (handler != null)
        handler.process(currentPage);
      currentPage = null;
    } else if (inpage && currentState.size() == 3 && currentState.get(2).equals(qName))
    {
      if (currentSB != null)
      {
        currentPage.put(currentState.get(currentState.size() - 1), currentSB.toString());
      }
      currentSB = null;
    } else if (inpage && currentState.size() == 4 && currentState.get(3).equals(qName) && "text".equals(qName))
    {
      currentPage.put(currentState.get(currentState.size() - 1), currentSB.toString());
      currentSB = null;
    }
    currentState.remove(currentState.size() - 1);
  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#characters(char[], int, int)
   */
  @Override
  public void characters(char[] ch, int start, int length) throws SAXException
  {
    if (currentSB != null)
      currentSB.append(ch, start, length);
  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
   */
  @Override
  public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
  {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String,
   * java.lang.String)
   */
  @Override
  public void processingInstruction(String target, String data) throws SAXException
  {
    // TODO Auto-generated method stub

  }

  /*
   * (non-Javadoc)
   * 
   * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
   */
  @Override
  public void skippedEntity(String name) throws SAXException
  {
    // TODO Auto-generated method stub

  }

  public static void main(String[] args) throws SAXException, IOException, InterruptedException
  {
    WikiXmlEngine engine = new WikiXmlEngine(args[0]);
    WikiPageAsyncPrintHandler[] handlers = new WikiPageAsyncPrintHandler[10];
    for (int i = 0; i < handlers.length; i++)
    {
      handlers[i] = new WikiPageAsyncPrintHandler("test", i);
      handlers[i].start();
    }
    WikiPageHandler handler = new WikiPageMultiHandler(handlers);
    handler.setFilter(new WikiXMLPageValidArticleFilter());
    engine.setHandler(handler);
    engine.process();
    for (int i = 0; i < handlers.length; i++)
    {
      handlers[i].drain();
      handlers[i].join();
    }
  }
}
